在上一篇文章中,我們建立了專案的基礎目錄結構。你可能會想,為什麼要分得這麼細?不能直接在 main.go
裡處理所有事情嗎?
問得好。答案在於我們的目標:打造一個可維護、可測試、可擴展的「API」應用。為了達到這個目標,我們需要一個優良的軟體架構來指導我們,而我們選擇的就是六角形架構(Hexagonal Architecture)。
六角形架構,又稱為Ports and Adapters,或廣義上的Clean Architecture,是由 Alistair Cockburn 提出的一種軟體架構模式。
它的核心思想非常簡單:
保護你的核心業務邏輯,使其與外部世界(例如 UI、資料庫、第三方服務)完全解耦。
想像你的應用程式是一個城堡。城堡最核心、最寶貴的是國王和他的寶藏(也就是你的業務邏輯)。城堡需要與外界溝通(例如接收糧食、派遣軍隊),但你不想讓任何人隨便就闖進來。於是,你在城堡上開了幾個固定的城門(Ports),並規定了所有進出的規則。任何想跟城堡打交道的人,都必須透過這些城門,並使用你指定的交通工具(Adapters)。
對應到我們的程式碼中:
內部核心 (Inside / Domain):應用程式的核心業務邏輯。它包含了 Usecase
(業務流程)和 Domain
(業務實體)。這部分程式碼是純粹的,它不應該知道任何外部技術的細節。它不知道資料是存在 MySQL 還是 MongoDB,也不知道請求是來自 HTTP API 還是命令列。
Ports:是內部核心與外部世界溝通的介面(Interface)。它定義了「需要做什麼」,但不關心「如何做」。例如,一個 Repository
埠可能定義了 CreateUser
和 GetUserByID
這兩個方法,但它不關心具體是用 GORM 還是原生 SQL 來實現。
Adapters:是 Ports 的具體實現,是連接外部技術和我們核心業務的「橋樑」。
Handler
就是,它接收 HTTP 請求,然後呼叫核心的 Usecase
。Repository
Ports 的程式碼就是。核心業務邏輯透過 Ports 呼叫它,它負責與 MySQL 資料庫進行溝通。六角形架構能成功的關鍵在於依賴關係倒轉原則(Dependency Inversion Principle)。
所有依賴關係都必須指向內部核心。
這意味著:
這就是為什麼我們需要「Ports」(Interface)。Usecase
依賴的是 Repository
這個Interface(屬於核心的一部分),而不是 GORM 的具體實作(屬於 Adapters)。這樣,核心就保持了它的純粹性。
極高的可測試性:我們可以輕易地為核心業務邏輯撰寫單元測試,因為它不依賴任何外部服務。我們只需要建立一個「假的」Adapter(Mock Adapter)來模擬資料庫行為,就可以獨立測試業務流程的正確性。
技術無關性與靈活性:我們的核心業務邏輯與外部技術解耦。這意味著,如果我們未來想把 Web 框架從 Gin 換成 Echo,或者把資料庫從 MySQL 換成 PostgreSQL,我們需要做的僅僅是更換或新增一個 Adapter,而核心業務邏輯完全不需要改動。
清晰的職責分離:架構強迫我們思考每一段程式碼的職責。Controller
只負責處理 HTTP 請求與回應,Usecase
只負責業務流程,Repository
只負責資料庫存取。這讓程式碼更容易理解和維護。
現在,再看我們設計的目錄結構,就清晰多了:
internal/domain
: 核心中的核心。定義業務實體和規則。internal/usecase
: 核心業務邏輯。它會定義 Port(Interface),並依賴 domain
。internal/controller
: Primary Adapter。處理 HTTP 請求,依賴 usecase
。internal/database/mysql
: Secondary Adapter。實現 usecase
中定義的資料庫 Port,與資料庫互動。依賴方向永遠是:controller
-> usecase
<- database/mysql
,而 usecase
和 database/mysql
都指向 domain
。
六角形架構初看起來可能有些複雜,甚至對於小型專案來說有點「殺雞用牛刀」。但它所帶來的長期效益是巨大的。它強迫我們寫出低耦合、高內聚、易於測試和維護的程式碼。
養成這種架構思維,是從「寫能跑的程式」到「寫能活得久的產品」的關鍵一步。